package com.mymarkdown.app.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.mymarkdown.app.common.constant.Constant;
import com.mymarkdown.app.common.enums.RightTreeTypeEnum;
import com.mymarkdown.app.fx.FxApplication;
import com.mymarkdown.app.fx.assemblys.FxTreeItem;
import com.mymarkdown.app.fx.enums.FxTreeItemTypeEnum;
import com.mymarkdown.app.interfaces.ModifyWindow;
import com.mymarkdown.app.interfaces.UserInfoService;
import com.mymarkdown.app.model.AppWindowInfo;
import com.mymarkdown.app.model.UserLatelyInfo;
import com.mymarkdown.app.service.impl.DefaultUserOperationFileService;
import com.mymarkdown.app.websocket.interfaces.WebSocketSendMessage;
import com.mymarkdown.app.websocket.message.MessagePojo;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.paint.Paint;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import javax.websocket.Session;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

/**
 * @author zenglun
 * @date 2021/12/5 9:14 下午
 * @description: 记录当前用户信息
 */
@Getter
@Setter
@Slf4j
@Accessors(chain = true)
public class CurrentUserUtil implements WebSocketSendMessage, UserInfoService<CurrentUserUtil>, ModifyWindow<CurrentUserUtil> {

    private BlockingQueue<File> operationFile = new DefaultUserOperationFileService(new ArrayBlockingQueue<>(1), this, this);

    private Session session;

    private UserLatelyInfo userLatelyInfo = UserLatelyInfo.empty();

    private BlockingQueue<String> operationMdHTML = new ArrayBlockingQueue<>(1);

    private BlockingQueue<File> operationMdPdfFile = new ArrayBlockingQueue<>(1);

    private AppWindowInfo appWindowInfo = AppWindowInfo.getInstance(this);

    private static CurrentUserUtil self;



    private File workDir;

    private CurrentUserUtil() {
    }



    public static CurrentUserUtil getSelf() {
        if (self == null) {
            synchronized (CurrentUserUtil.class) {
                if (self == null) {
                    self = new CurrentUserUtil();
                }
            }
        }
        return self;
    }

    @Override
    public void setSession(Session session) {
        this.session = session;
    }

    @Override
    public BlockingQueue<String> getOperationMdHTML() {
        return operationMdHTML;
    }

    @Override
    public CurrentUserUtil setFileSaveState(boolean state) {
        appWindowInfo.setSave(state);
        return this;
    }


    @Override
    public boolean sendMessage(MessagePojo messagePojo) {
        if (Objects.isNull(messagePojo)) {
            return false;
        }
        return sendMessage(JSON.toJSONString(messagePojo));
    }

    private boolean sendMessage(String msg) {
        if (Objects.isNull(session)) {
            return false;
        }
        try {
            session.getBasicRemote().sendText(msg);
        } catch (IOException e) {
            log.error("[CurrentUserUtil]---[sendMessage],发生异常,sessionId为:{},msg:{}", msg);
            return false;
        }
        return true;
    }

    @Override
    public CurrentUserUtil changeTitle() {
        AppWindowInfo appWindowInfo = CurrentUserUtil.getSelf().getAppWindowInfo();
        String filename = appWindowInfo.getFilename();
        if (StrUtil.isBlank(filename)) {
            //如果文件名称为空
            Platform.runLater(() -> FxApplication.appendTitle(Constant.NOT_FILE_SHOW_TEXT));
        } else {
            String text = appWindowInfo.isSave() ? "已保存" : "未保存";
            Platform.runLater(() -> FxApplication.appendTitle(String.format(Constant.HAS_FILE_SHOW_TITLE_FORMAT + Constant.SAVE_FILE_TEXT,
                    appWindowInfo.getFilename(), text)));
        }
        return this;
    }


    /**
     * 加载历史操作数据
     */
    @Override
    public void loadHistoryOperationTree() {
        List<String> historyOperationFile = this.userLatelyInfo.getHistoryOperationFile();
        if (CollectionUtil.isNotEmpty(historyOperationFile)) {
            TreeItem<String> root = FxApplication.getTreeView().getRoot();
            ObservableList<TreeItem<String>> children = root.getChildren();
            children.stream().map(e -> (FxTreeItem) e).filter(e -> FxTreeItemTypeEnum.HISTORICAL_OPERATION.equals(e.getTreeItemTypeEnum())).findAny().ifPresent(e -> {
                historyOperationFile.forEach(h -> {
                    File file = new File(h);
                    FxTreeItem<String> fileTreeItem = FxTreeItem.getFileTreeItem(file.getName(), file);
                    if (!file.exists()) {
                        fileTreeItem.getText().setFill(Paint.valueOf("rgb(220,220,220)"));
                        fileTreeItem.getText().setStrikethrough(true);
                    }
                    e.getChildren().add(fileTreeItem);
                });
            });
        }
    }

    @Override
    public void loadWorkDirTree() {
        File peek = this.getWorkDir();
        if (!Objects.isNull(peek) && peek.exists()) {
            TreeItem<String> root = FxApplication.getTreeView().getRoot();
            ObservableList<TreeItem<String>> children = root.getChildren();
            children.stream().forEach(item -> {
                FxTreeItem fxTreeItem = (FxTreeItem) item;
                if (fxTreeItem.isClear()) {
                    fxTreeItem.getChildren().clear();
                }
            });
            //加载所有的文件
            children.stream().map(e -> (FxTreeItem) e).filter(e -> FxTreeItemTypeEnum.CURRENT_OPERATION_FILE_LIST.equals(e.getTreeItemTypeEnum())).findAny().ifPresent(e -> {
                //添加返回上一级树
                getAllCurrentFileBuildTree(e, peek);
            });
        }

    }

    @Override
    public void addHistoryOperationTreeItem(String path) {
        if (StrUtil.isEmpty(path)) {
            return;
        }
        File file = new File(path);
        if (!file.exists()) {
            return;
        }
        //获取历史操作数据
        TreeItem<String> root = FxApplication.getTreeView().getRoot();
        ObservableList<TreeItem<String>> children = root.getChildren();
        children.stream().map(e -> (FxTreeItem) e).filter(e -> FxTreeItemTypeEnum.HISTORICAL_OPERATION.equals(e.getTreeItemTypeEnum())).findAny().ifPresent(e -> {
            e.getChildren().add(FxTreeItem.getFileTreeItem(file.getName(), file));
        });
    }

    @Override
    public void removeHistoryTreeItem(FxTreeItem<String> treeItem) {
        if (Objects.isNull(treeItem)) {
            return;
        }
        File sourceFile = treeItem.getSourceFile();
        if (Objects.isNull(sourceFile)) {
            return;
        }
        try {
            //移除历史
            this.getUserLatelyInfo().removeHistoryOperationFile(sourceFile.getPath());
            TreeItem<String> parent = treeItem.getParent();
            parent.getChildren().remove(treeItem);
        } catch (Exception e) {
        }
    }


    @Override
    public void getAllCurrentFileBuildTree(TreeItem<String> root, File file) {
        if (Objects.isNull(root) || Objects.isNull(file)) {
            return;
        }
        File[] files = file.listFiles();
        if (files == null || files.length == 0) {
            return;
        }

        Stream.of(files).forEach(e -> {
            //构建数
            if (e.isDirectory() && e.exists()) {
                FxTreeItem<String> fxTreeItem = FxTreeItem.getDirTreeItem(e.getName(), e);
                root.getChildren().add(fxTreeItem);
            }
            if (e.isFile() && e.exists() && StrUtil.equals(FileUtil.extName(e), "md")) {
                FxTreeItem<String> fxTreeItem = FxTreeItem.getFileTreeItem(e.getName(), e);
                root.getChildren().add(fxTreeItem);
            }
        });
    }

    @Override
    public FxTreeItem getTreeItemByPath(String path) {
        TreeView treeView = FxApplication.getTreeView();
        TreeItem<String> root = treeView.getRoot();
        return getTreeItemByPath(path, root);
    }

    @Override
    public synchronized void addTreeItemByPath(String path) {
        //找到父节点
        Platform.runLater(() -> {
            File file = new File(path);
            String parent = file.getParent();
            FxTreeItem treeItemByPath = getTreeItemByPath(parent);
            //判断是否是文件或者文件夹
            FxTreeItem addTreeItem = file.isFile() ? FxTreeItem.getFileTreeItem(file.getName(), file) : FxTreeItem.getDirTreeItem(file.getName(), file);
            //如果找到的不等于null
            if (!Objects.isNull(treeItemByPath)) {
                treeItemByPath.getChildren().add(addTreeItem);
            } else {
                //如果等于null,则表示可能是根目录,则找到文件列表
                TreeView treeView = FxApplication.getTreeView();
                treeView.getRoot().getChildren().stream().filter(e -> {
                    FxTreeItem fxTreeItem = (FxTreeItem) e;
                    return FxTreeItemTypeEnum.CURRENT_OPERATION_FILE_LIST.equals(fxTreeItem.getTreeItemTypeEnum());
                }).findAny().ifPresent(e -> {
                    FxTreeItem fxTreeItem = (FxTreeItem) e;
                    fxTreeItem.getChildren().add(addTreeItem);
                });
            }
        });
    }

    @Override
    public synchronized void removeItemByPath(String path) {
        Platform.runLater(() -> {
            File file = new File(path);
            FxTreeItem removeItem = getTreeItemByPath(path);
            if (Objects.isNull(removeItem)) {
                return;
            }
            String parent = file.getParent();
            FxTreeItem treeItemByPath = getTreeItemByPath(parent);
            if (Objects.nonNull(treeItemByPath)) {
                treeItemByPath.getChildren().remove(removeItem);
            } else {
                TreeView treeView = FxApplication.getTreeView();
                treeView.getRoot().getChildren().stream().filter(e -> {
                    FxTreeItem fxTreeItem = (FxTreeItem) e;
                    return FxTreeItemTypeEnum.CURRENT_OPERATION_FILE_LIST.equals(fxTreeItem.getTreeItemTypeEnum());
                }).findAny().ifPresent(e -> {
                    FxTreeItem fxTreeItem = (FxTreeItem) e;
                    //无法保存百分之百删除
                    if (!fxTreeItem.getChildren().remove(removeItem)) {
                        loadWorkDirTree();
                        //重新设置展开状态
                    }
                });
            }
        });
    }




    private FxTreeItem getTreeItemByPath(String path, TreeItem<String> root) {
        if (Objects.isNull(root)) {
            return null;
        }
        if (root instanceof FxTreeItem) {
            File sourceFile = ((FxTreeItem) root).getSourceFile();
            if (!Objects.isNull(sourceFile)) {
                String url = sourceFile.getPath();
                if (StrUtil.equals(url, path)) {
                    return (FxTreeItem) root;
                }
            }
        }
        Optional<FxTreeItem> any = root.getChildren().stream().map(item -> getTreeItemByPath(path, item)).filter(Objects::nonNull).findAny();
        return any.orElse(null);
    }


    @Override
    public File getWorkDir() {
        if (Objects.isNull(this.workDir) || !this.workDir.exists()) {
            BlockingQueue<File> operationFile = this.getOperationFile();
            File peek = operationFile.peek();
            if (!Objects.isNull(peek) && peek.getParentFile().exists()) {
                this.workDir = peek.getParentFile();
            }
        }
        return this.workDir;
    }

    @Override
    public CurrentUserUtil setWorkDir(File file) {
        this.workDir = file;
        return this;
    }
}
